package com.liaoyifan.core.thread;

import com.liaoyifan.core.util.RequestContext;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;

@SuppressWarnings("unused")
public class ThreadFactoryBuilder {
    private String nameFormat;
    private Boolean daemonThread;
    private Integer priority;
    private Thread.UncaughtExceptionHandler uncaughtExceptionHandler = null;
    private ThreadFactory backingThreadFactory = null;

    private ThreadFactoryBuilder() {}

    public static ThreadFactoryBuilder builder() {
        return new ThreadFactoryBuilder();
    }

    public ThreadFactoryBuilder nameFormat(String nameFormat) {
        this.nameFormat = nameFormat;
        return this;
    }

    public ThreadFactoryBuilder daemon(boolean daemon) {
        this.daemonThread = daemon;
        return this;
    }

    public ThreadFactoryBuilder priority(int priority) {
        this.priority = priority;
        return this;
    }

    public ThreadFactoryBuilder uncaughtExceptionHandler(
            Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
        this.uncaughtExceptionHandler = Objects.requireNonNull(uncaughtExceptionHandler);
        return this;
    }

    public ThreadFactoryBuilder backingThreadFactory(ThreadFactory backingThreadFactory) {
        this.backingThreadFactory = Objects.requireNonNull(backingThreadFactory);
        return this;
    }

    public ThreadFactory build() {
        return build(this);
    }

    private static ThreadFactory build(ThreadFactoryBuilder builder) {
        String nameFormat = builder.nameFormat;
        Boolean daemon = builder.daemonThread;
        Integer priority = builder.priority;
        Thread.UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler;
        ThreadFactory backingThreadFactory =
                builder.backingThreadFactory != null
                        ? builder.backingThreadFactory
                        : Executors.defaultThreadFactory();
        AtomicLong count = nameFormat != null ? new AtomicLong(0L) : null;
        return (runnable) -> {
            Thread thread = backingThreadFactory.newThread(RequestContext.init(runnable));
            if (nameFormat != null) {
                String name = String.format(nameFormat, count.getAndIncrement());
                thread.setName(name);
            }

            if (daemon != null) {
                thread.setDaemon(daemon);
            }

            if (priority != null) {
                thread.setPriority(priority);
            }

            if (uncaughtExceptionHandler != null) {
                thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
            }

            return thread;
        };
    }
}
